
package at.fhj.ase.clientarch.server.db.dao.impl;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.fhj.ase.clientarch.common.exception.AlreadyInitializedException;
import at.fhj.ase.clientarch.server.db.dao.HardwareDataDAO;
import at.fhj.ase.clientarch.server.db.dao.UserDAO;
import at.fhj.ase.clientarch.server.db.hibernate.entities.EntityHardwareData;
import at.fhj.ase.clientarch.server.sigar.SigarController;
import at.fhj.ase.clientarch.server.util.JPAUtil;

/**
 * Class as the sole touching point between database and application.<br>
 * All data access opjects are created and controller here, also the {@link Timer} task that writes continuously into database.
 * 
 * @author cja, sfe | www.fh-joanneum.at | Client Architectures and Design
 */
public enum DatabaseController {
  INSTANCE;

  private class DatabaseTimerTask extends TimerTask {
    private int writeCounter = 0;
    private final List<EntityHardwareData> dataList = new ArrayList<EntityHardwareData>();

    @Override
    public void run() {
      dataList.add(new EntityHardwareData(SigarController.INSTANCE.getCurrentData()));
      writeCounter++;

      // TODO make this value configurable
      if (writeCounter == 20) {
        getHardwareDataDao().insertList(dataList);

        // reset the counter and clear the list
        writeCounter = 0;
        dataList.removeAll(dataList);
      }
    }
  }

  private static final String KEY_CHOSEN_DATABASE_CONFIG = "chosen.database.configuration";
  private static final String DEFAULT_CHOSEN_DATABASE = JPAUtil.DEFAULT_PERSISTENCE_TYPE;

  private static final long TIMER_START_DELAY_MILLIS = 2000;
  private static final long TIMER_REPEAT_TIME_MILLIS = 500;

  private final Logger logger = LoggerFactory.getLogger(getClass().getName());

  private UserDAO userDao;
  private HardwareDataDAO hardwareDataDao;
  private Timer runningTimer;

  public void init() {
    //TODO we should not stick on the timer and create a property for that case, let's use one of the DAOs for the check as they are already existent as properties
    if (runningTimer != null) {
      throw new AlreadyInitializedException("DatabaseController has already been initialized");
    }
    initPersistence();
    initDAOs();
  }

  public void startTimer() {
    if (runningTimer != null) {
      throw new AlreadyInitializedException("DatabaseController has already been initialized");
    }
    runningTimer = new Timer(true);
    runningTimer.scheduleAtFixedRate(new DatabaseTimerTask(), TIMER_START_DELAY_MILLIS, TIMER_REPEAT_TIME_MILLIS);
  }

  private void initPersistence() {
    String chosenDatabaseConfig = null;
    try {
      final Properties p = new Properties();
      final FileInputStream fis = new FileInputStream("cfg/databasechooser.properties");

      p.load(fis);
      fis.close();
      chosenDatabaseConfig = p.getProperty(KEY_CHOSEN_DATABASE_CONFIG);
    } catch (Exception e) {
      logger.error("Could not read property due to following error, using standard...", e);
      chosenDatabaseConfig = DEFAULT_CHOSEN_DATABASE;
    }

    JPAUtil.createSessionFactory(chosenDatabaseConfig);
    logger.debug("Created database with config " + chosenDatabaseConfig);
  }

  private void initDAOs() {
    userDao = new UserDAOImpl();
    hardwareDataDao = new HardwareDataDAOImpl();
  }

  public UserDAO getUserDao() {
    return userDao;
  }

  public HardwareDataDAO getHardwareDataDao() {
    return hardwareDataDao;
  }
}
